﻿using System;
using System.Collections.Generic;
using System.Text;
using ITAP.ShprossesBuilder.Modelling.Components.Constraints;
using ITAP.Core.Modelling.Primitives;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Modelling.Components
{           
    [Serializable]
    public class SnapOnCap : IComponent
    {
        public double this[int i]
        {
            get
            {
                return ModelCalculations.GetDistance(this.initialCenter, this.initialPoints[i]);
            }
        }

        private bool uniqueIdsEnabled = true;

        public bool UniqueIdsEnabled
        {
            get { return uniqueIdsEnabled; }
            set { uniqueIdsEnabled = value; }
        }

        private List<int> acceptedUniqueIdsToConnect;

        public List<int> AcceptedUniqueIdsToConnect
        {
            get { return acceptedUniqueIdsToConnect; }
            set { acceptedUniqueIdsToConnect = value; }
        }

        private int uniqueId;

        public int UniqueId
        {
            get { return uniqueId; }
            set { uniqueId = value; }
        }

        private bool inaccessibleFromMaterials = false;

        public bool InaccessibleFromMaterials
        {
            get { return inaccessibleFromMaterials; }
            set { inaccessibleFromMaterials = value; }
        }


        private string name;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private int serialNumber;

        public int SerialNumber
        {
            get { return serialNumber; }
            set { serialNumber = value; }
        }

        private int opacity = 100;

        public int Opacity
        {
            get { return opacity; }
            set { opacity = value; }
        }

        protected Color color;

        public Color Color
        {
            get { return color; }
            set { color = value; }
        }
        protected Color borderColor;

        public Color BorderColor
        {
            get { return borderColor; }
            set { borderColor = value; }
        }

        private ModelPoint initialCenter;

        public ModelPoint InitialCenter
        {
            get { return initialCenter; }
            set { initialCenter = value; }
        }
        private List<ModelPoint> initialPoints;

        public List<ModelPoint> InitialPoints
        {
            get { return initialPoints; }
            set { initialPoints = value; }
        }

        private bool autoMount = false;

        public bool AutoMount
        {
            get { return autoMount; }
            set { autoMount = value; }
        }

        public int EndsCount
        {
            get { return initialPoints.Count; }
        }

        private double capWidth;

        public double CapWidth
        {
            get { return capWidth; }
            set { capWidth = value; }
        }

        private double capLength;

        public double CapLength
        {
            get { return capLength; }
        }

        private List<AngleInterval> allowedAngleIntervals;

        public List<AngleInterval> AllowedAngleIntervals
        {
            get { return allowedAngleIntervals; }
        }

        public SnapOnCap(double capWidth, Color color, Color borderColor)
        {
            this.capWidth = capWidth;
            this.capLength = capLength;
            this.acceptedComponentTypesToConnect = new ConnectionInfo();
            this.componentsInTolerance = new List<IComponent>();
            this.connectedComponents = new List<IComponent>();
            this.constraints = new List<IConstraint>();
            this.innerComponents = new List<IComponent>();
            this.primitives = new List<IPrimitive>();
            this.color = color;
            this.borderColor = borderColor;
            this.allowedAngleIntervals = new List<AngleInterval>();
        }

        public SnapOnCap(double capWidth, double capLength, Color color, Color borderColor)
            : this(capWidth, color, borderColor)
        {
            this.capLength = capLength;
        }

        public void Recreate(ModelPoint center, ModelPoint[] endPoints)
        {
            this.Primitives.Clear();
            this.Primitives.Add(PrimitiveFactory.CreateStubPolygon(center, endPoints, capWidth));
            this.initialCenter = (ModelPoint)center.Clone();
            this.initialPoints = new List<ModelPoint>();
            foreach (ModelPoint point in endPoints)
                this.initialPoints.Add((ModelPoint)point.Clone());
        }

        public bool IsAngleIntervalsCorrect()
        {
            //if (this.EndsCount != allowedAngleIntervals.Count + 1)
            //    throw new Exception();

            ModelVector startVector = new ModelVector(this.initialCenter, this.initialPoints[0]);
            
            for (int i = 1; i < this.initialPoints.Count; i++)
            {
                ModelVector initialPointVector = new ModelVector(this.initialCenter, this.initialPoints[i]);
                Angle angle = ModelCalculations.GetAngleBetweenVectors(startVector, initialPointVector, false);
                if (!allowedAngleIntervals.IsInIntervals(angle))
                    return false;
                //if (!allowedAngleIntervals[i - 1].IsAngleInInterval(angle))
                //    return false;
            }
            return true;
        }

        #region IComponent Members

        private List<IConstraint> constraints;
        public List<ITAP.ShprossesBuilder.Modelling.Components.Constraints.IConstraint> Constraints
        {
            get { return constraints; }
        }

        private List<IComponent> connectedComponents;
        public List<IComponent> ConnectedComponents
        {
            get { return connectedComponents; }
        }

        private List<IComponent> innerComponents;
        public List<IComponent> InnerComponents
        {
            get { return innerComponents; }
        }

        public List<IComponent> AllInnerComponents
        {
            get { return new List<IComponent>(); }
        }

//        private double tolerance = 5;
        public double Tolerance
        {
            get
            {
                return this.BoundingBox.Width / 2;
            }
            set
            {
                //tolerance = value;
            }
        }

        public System.Drawing.RectangleF BoundingBox
        {
            get { return this.Primitives[0].BoundingBox; }
        }

        private bool isUpdateRequired = true;
        public bool IsUpdateRequired
        {
            get
            {
                return isUpdateRequired;
            }
            set
            {
                isUpdateRequired = value;
            }
        }

        private List<IPrimitive> primitives;
        public List<ITAP.Core.Modelling.Primitives.IPrimitive> Primitives
        {
            get { return primitives; }
        }

        public IComponent ConnectTo(IComponent component)
        {
            if (!this.connectedComponents.Contains(component) && 
                this.acceptedComponentTypesToConnect.Contains(component.GetType()))
            {
                this.connectedComponents.Add(component);
                return this;
            }
            else
                return null;
        }

        public IComponent DisconnectFrom(IComponent component)
        {
            if (this.connectedComponents.Contains(component))
            {
                this.connectedComponents.Remove(component);
                return this;
            }
            else
                throw new Exception();
        }

        public bool CanConnect(IComponent component)
        {
            if (this.acceptedComponentTypesToConnect.Contains(component.GetType()))
                return true;
            else
                return false;
        }

        public void Update()
        {
            throw new NotImplementedException();
        }

        public void AcceptVisitor(IModelVisitor visitor)
        {
            visitor.Visit(this);
        }

        public bool IsInTolerance(ModelPoint point)
        {
            if(ModelCalculations.GetDistance(this.Position, point) < this.Tolerance)
                return true;
            else
                return false;
        }

        public List<IComponent> GetComponentsByPoint(ModelPoint point, double tolerance)
        {
            throw new NotImplementedException();
        }

        public bool MoveTo(ModelPoint destination)
        {
            ModelVector offsetVector = new ModelVector(destination.X - this.Position.X, destination.Y - this.Position.Y);
            this.primitives[0].MoveWithOffset(offsetVector);
            return true;
        }

        public bool MoveWithOffset(ModelVector offset)
        {
            this.Primitives[0].MoveWithOffset(offset);
            return true;
        }

        public bool Rotate(double angleOffset, ModelPoint rotateCenter)
        {
            this.Primitives[0].Rotate(rotateCenter, angleOffset);
            return true;
          //  throw new NotImplementedException();
        }

        public ModelPoint Position
        {
            get 
            {
                return ((IEndlessPrimitive)this.Primitives[0]).Center;
            }
        }

        public ITAP.Core.Modelling.Primitives.EndType GetComponentNearestEndType(IComponent component)
        {
            throw new NotImplementedException();
        }

        public List<Type> PreUpdatePriority
        {
            get { throw new NotImplementedException(); }
        }

        public List<Type> PostUpdatePriority
        {
            get { throw new NotImplementedException(); }
        }

        private IComponent parent;
        public IComponent Parent
        {
            get
            {
                return parent;
            }
            set
            {
                parent = value;
            }
        }


        //public virtual bool CheckCriticalConstraints()
        //{
        //    foreach (IConstraint constraint in this.constraints)
        //        if (this.criticalConstraintTypes.Contains(constraint.GetType()))
        //            if (constraint.Check(this) == false)
        //                return false;

        //    return true;
        //}

        //public virtual bool CheckAllConstraints()
        //{
        //    if (!this.CheckConstraints())
        //        return false;

        //    foreach (IComponent innerComponent in this.InnerComponents)
        //        if (!innerComponent.CheckAllConstraints())
        //            return false;

        //    return true;
        //}

        //public virtual bool CheckAllCriticalConstraints()
        //{
        //    if (!this.CheckCriticalConstraints())
        //        return false;

        //    foreach (IComponent innerComponent in this.InnerComponents)
        //        if (!innerComponent.CheckAllCriticalConstraints())
        //            return false;

        //    return true;
        //}

        //public virtual bool CheckConstraints()
        //{
        //    foreach (IConstraint constraint in this.constraints)
        //        if (!constraint.Check(this))
        //            return false;

        //    return true;
        //}

        public bool EnableConstraintsFixing
        {
            get
            {
                foreach (IConstraint constraint in this.constraints)
                    if (constraint.Enabled == false)
                        return false;

                return true;
            }
            set
            {
                foreach (IConstraint constraint in this.constraints)
                    constraint.Enabled = value;
            }
        }

        public bool EnableAllConstraintsFixing
        {
            get
            {
                bool isEnabled = this.EnableConstraintsFixing;
                if (!isEnabled)
                    return false;

                foreach (IComponent component in this.innerComponents)
                    if (!component.EnableAllConstraintsFixing)
                        return false;

                return true;
            }
            set
            {
                this.EnableConstraintsFixing = value;

                foreach (IComponent component in this.innerComponents)
                    component.EnableAllConstraintsFixing = value;
            }
        }

        private List<Type> criticalConstraintTypes;

        public List<Type> CriticalConstraintTypes
        {
            get { return criticalConstraintTypes; }
        }
        private ConnectionInfo acceptedComponentTypesToConnect;
        public ConnectionInfo AcceptedComponentTypesToConnect
        {
            get { return acceptedComponentTypesToConnect; }
        }

        private string article;
        public string Article
        {
            get
            {
                return article;
            }
            set
            {
                article = value;
            }
        }

        public IComponent ConnectToEnded(IEndedComponent endedComponent, ITAP.Core.Modelling.Primitives.EndType endToConnect)
        {
            throw new NotImplementedException();
        }

        public event ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventDelegate BeforeFixingConstraint;

        public event ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventDelegate AfterFixingConstraint;

        public event FatalModelErrorDelegate FatalModelError;

        public bool IsConnected(IComponent other)
        {
            throw new NotImplementedException();
        }

        public void AddInnerComponent(IComponent component)
        {
            throw new NotImplementedException();
        }

        public bool RemoveInnerComponent(IComponent component)
        {
            throw new NotImplementedException();
        }

        public void BeforeFixingInnerComponentConstraintEvent(IComponent sender, ComponentConstraintEventArgs e)
        {
            if (this.BeforeFixingConstraint != null)
                this.BeforeFixingConstraint.Invoke(sender, e);
        }

        public void AfterFixingInnerComponentConstraintEvent(IComponent sender, ComponentConstraintEventArgs e)
        {
            if (this.AfterFixingConstraint != null)
                this.AfterFixingConstraint.Invoke(sender, e);
        }

        public void FatalModelErrorEvent(IComponent sender, FatalErrorEventArgs e)
        {
            if (this.FatalModelError != null)
                this.FatalModelError.Invoke(sender, e);
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            sticked = StickConnectionType.None;
            return new ModelPoint(double.NaN, double.NaN);
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, ModelPoint measurePoint, StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            sticked = StickConnectionType.None;
            return new ModelPoint(double.NaN, double.NaN);
        }

        private bool selected = false;

        public bool Selected
        {
            get
            {
                return selected;
            }
            set
            {
                selected = value;
            }
        }

        public bool IsPointInComponent(ModelPoint point)
        {
            throw new NotImplementedException();
        }
        private List<IComponent> componentsInTolerance;
        public List<IComponent> ComponentsInTolerance
        {
            get { return componentsInTolerance; }
        }

        private bool autoConnectoComponentsInTolerance = false;
        public bool AutoConnectComponentsInTolerance
        {
            get
            {
                return autoConnectoComponentsInTolerance;
            }
            set
            {
                autoConnectoComponentsInTolerance = value;
            }
        }

        public void DisconnectComponentsInTolerance()
        {
            throw new NotImplementedException();
        }

        public void DisconnectFromAll()
        {
            List<IComponent> componentsToDisconnect = new List<IComponent>();
            foreach (IComponent connectedComponent in this.ConnectedComponents)
            {
                componentsToDisconnect.Add(connectedComponent);
            }
            foreach (IComponent connectedComponent in componentsToDisconnect)
            {
                ConnectionProvider.Disconnect(this, connectedComponent);
            }
        }

        public void MoveToZeroPoint()
        {
            RectangleF bbox = this.BoundingBox;
            this.MoveWithOffset(new ModelVector(-bbox.X, -bbox.Y));
        }

        public void MoveToZeroXPoint()
        {
            RectangleF bbox = this.BoundingBox;
            this.MoveWithOffset(new ModelVector(-bbox.X, 0));
        }

        public void MoveToZeroYPoint()
        {
            RectangleF bbox = this.BoundingBox;
            this.MoveWithOffset(new ModelVector(0, -bbox.Y));
        }

        #endregion

        #region IModelVisitor Members

        public void Visit(Model model)
        {
           // throw new NotImplementedException();
        }

        public void Visit(Connector connector)
        {
        //    throw new NotImplementedException();
        }

        public void Visit(Shpross shpross)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(Spacer spacer)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(ShprossConnectorStub stub)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(ShprossConnector shprossConnector)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(SpacerElement spacerElement)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(SpacerConnector spacerConnector)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(ModelCell modelCell)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(ModelContainer modelContainer)
        {
          //  throw new NotImplementedException();
        }

        public void Visit(StickConnector stickConnector)
        {
          //  throw new NotImplementedException();
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            return this.Copy();

            //Stream stream = new MemoryStream();
            //BinaryFormatter binary = new BinaryFormatter();            
            //binary.Serialize(stream, this);
            //stream.Seek(0, SeekOrigin.Begin);
            //object clone = binary.Deserialize(stream);
            //stream.Close();
            //return clone;
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SnapOnCap snapOnCap)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool SelectByPoint(ModelPoint point)
        {
            PointF _point = new PointF((float)point.X, (float)point.Y);
            if (this.BoundingBox.Contains(_point))
            {

                this.selected = !this.selected;
                return true;
            }
            else
            {
                this.selected = false;
                return false;
            }
        }

        #endregion

        #region IComponent Members


        public void Rotate(double angleOffset)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public ModelPoint GetNearestPointOnComponent(ModelPoint point, StickConnectionType stickType, out StickConnectionType sticked, out IComponent innerComponent, ProjectionType projectionType)
        {
            throw new NotImplementedException();
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, ModelPoint measurePoint, StickConnectionType stickType, out StickConnectionType sticked, out IComponent innerComponent, ProjectionType projectionType)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool MoveTo(ModelPoint destination, RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        public bool MoveWithOffset(ModelVector offset, RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        #endregion

        //public void RemoveAllInnerComponents()
        //{
        //    foreach (IComponent innerComponent in this.innerComponents)
        //    {
        //        innerComponent.RemoveAllInnerComponents();
        //        this.RemoveInnerComponent(innerComponent);
        //    }
        //}



        #region IComponent Members


        public bool Rotate(double angleOffset, ModelPoint rotateCenter, RectangleF boundingRectangle)
        {
            this.Rotate(angleOffset, rotateCenter);
            if (!boundingRectangle.IsEmpty && !boundingRectangle.Contains(this.BoundingBox))
            {
                this.Rotate(-angleOffset, rotateCenter);
                return false;
            }
            return true;
        }

        #endregion

        #region IComponent Members


        public bool Rotate(double angleOffset, RectangleF boundingRectangle)
        {
            this.Rotate(angleOffset);
            if (!boundingRectangle.IsEmpty && !boundingRectangle.Contains(this.BoundingBox))
            {
                this.Rotate(-angleOffset);
                return false;
            }
            return true;
        }

        #endregion

        #region IComponent Members


        public bool IsInTolerance(ModelPoint point, double tolerance)
        {
            return this.primitives[0].IsInTolerance(point, tolerance);
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(ShprossEndToEndConnector endToEndConnector)
        {
         //   throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(ShprossChain shprossChain)
        {
          //  throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public IComponent Copy()
        {
            SnapOnCap copy = new SnapOnCap(this.capWidth, this.color, this.borderColor);
            foreach (AngleInterval interval in this.allowedAngleIntervals)
                copy.allowedAngleIntervals.Add(interval.Clone() as AngleInterval);
            copy.capLength = this.capLength;
            
            copy.Recreate(this.initialCenter, this.initialPoints.ToArray());
            copy.article = this.article;
            copy.MoveTo(this.Position);
            copy.serialNumber = this.serialNumber;
            copy.uniqueId = this.uniqueId;
            copy.acceptedUniqueIdsToConnect = new List<int>(this.acceptedUniqueIdsToConnect);
            foreach (ConnectionInfoItem info in this.AcceptedComponentTypesToConnect)
                copy.AcceptedComponentTypesToConnect.Add(info);            
            copy.autoMount = this.autoMount;

            foreach (IConstraint constraint in this.constraints)
                copy.Constraints.Add(constraint.Clone() as IConstraint);

            copy.name = this.name;
            copy.opacity = this.opacity;

            return copy;

        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SpacerContainer spacerContainer)
        {
         //   throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(CrossConnector crossConnector)
        {
      //      throw new NotImplementedException();
        }

        #endregion
    }
}
